<?php
/**
 * Created by PhpStorm.
 * User: 65736
 * Date: 2019/4/4 0004
 * Time: 20:48
 */

namespace app\common\library;


use think\Log;

ini_set('gd.jpeg_ignore_warning', 1);
/**
 * 图像处理类
 * Class ImageKuPHP
 * @package app\common\library
 */
class ImageKuPHP
{
    /**
     * 图像处理库，目前只支持gd2
     * @var string
     */
    protected $m_image_library = 'gd2';
    /**
     * 源图片名
     * @var string
     */
    protected $m_source_image = '';
    /**
     * 设置的新图片名
     * @var string
     */
    protected $m_new_image = '';
    /**
     * 新图片名
     * @var string
     */
    protected $m_dest_image = '';
    /**
     * 新图片宽度
     * @var int
     */
    protected $m_width = 0;
    /**
     * 新图片高度
     * @var int
     */
    protected $m_height = 0;
    /**
     * 图片质量
     * @var int
     */
    protected $m_quality = 90;
    /**
     * 是否创建缩略图，如果是false，并且不提供new_name的话，会覆盖源文件
     * @var bool
     */
    protected $m_create_thumb = false;
    /**
     * 缩略图后缀
     * @var string
     */
    protected $m_thumb_marker = '_thumb';
    /**
     * 是否保持原图宽高比
     * @var bool
     */
    protected $m_maintain_ratio = true;
    /**
     * 如果新的比例与原比例不相同，以宽还是高为准，如果是auto则自动判断
     * @var string
     */
    protected $m_master_dim = 'auto';
    /**
     * 旋转角度，逆时针
     * @var int
     */
    protected $m_rotation_angle = 0;
    /**
     * x轴坐标
     * @var int
     */
    protected $m_x_axis = 0;
    /**
     * y轴坐标
     * @var int
     */
    protected $m_y_axis = 0;

    /**
     * 原图片目录
     * @var string
     */
    private $m_source_folder = '';
    /**
     * 目标图片目录
     * @var string
     */
    private $m_dest_folder = '';
    /**
     * Mime
     * @var string
     */
    private $m_mime_type = '';
    /**
     * 原始图片宽度
     * @var int
     */
    private $m_orig_width = 0;
    /**
     * 原始图片高度
     * @var int
     */
    private $m_orig_height = 0;
    /**
     * 图片类型
     * @var string
     */
    private $m_image_type = '';
    /**
     * 图片尺寸
     * @var string
     */
    private $m_size_str = '';
    /**
     * 原图完整路径
     * @var string
     */
    private $m_full_src_path = '';
    /**
     * 新图完整路径
     * @var string
     */
    private $m_full_dst_path = '';

    /**
     * @var string
     */
    private $m_create_fnc = 'imagecreatetruecolor';

    /**
     * @var string
     */
    private $m_copy_fnc = 'imagecopyresampled';

    /**
     * 错误信息
     * @var array
     */
    private $m_error_msg = array();
    /**
     * 新文件的文件权限
     * @var int
     */
    private $m_file_write_mode = 0755;


    /**
     * 构造函数
     * @param array $config
     */
    public function __construct($config=array()){
        foreach ($config as $k=>$v){
            $key = "m_".$k;
            $this->$key = $v;
        }
        $this->init();
    }

    /**
     * 设置错误信息
     * @param mixed $msg	错误信息
     */
    public function set_error($msg) {
        if (is_array($msg)) {
            foreach ($msg as $val) {
                $this->m_error_msg[] = $val;
                Log::error($val);
            }
        }
        else {
            $this->m_error_msg[] = $msg;
            Log::error($msg);
        }
    }

    /**
     * 读取错误信息
     * @return array
     */
    public function get_error(){
        return $this->m_error_msg;
    }

    public function get_image_properties($path = '', $return=false) {
        // For now we require GD but we should
        // find a way to determine this using IM or NetPBM

        if ( $path=='' ){
            $path = $this->m_full_src_path;
        }
        if ( !is_file($path) ) {
            $this->set_error('源图片不存在');
            return false;
        }
        $vals = @getimagesize($path);
        $types = array(1=>'gif', 2=>'jpeg', 3=>'png');
        $mime = (isset($types[$vals['2']])) ? 'image/'.$types[$vals['2']] : 'image/jpg';
        if ( $return==true ) {
            $v = array();
            $v['width']	= $vals['0'];
            $v['height'] = $vals['1'];
            $v['image_type'] = $vals['2'];
            $v['size_str'] = $vals['3'];
            $v['mime_type'] = $mime;
            return $v;
        }
        $this->m_orig_width = $vals[0];
        $this->m_orig_height = $vals[1];
        $this->m_image_type = $vals[2];
        $this->m_mime_type = $mime;
        return true;
    }

    /**
     * 获得GD的版本
     * @return mixed|boolean
     */
    public function gd_version() {
        if ( function_exists('gd_info') ) {
            $gd_version = @gd_info();
            $gd_version = preg_replace('/\D/', '', $gd_version['GD Version']);
            return $gd_version;
        }
        return false;
    }

    /**
     * 获得文件名中的名称和扩展名
     * @param string $source_image
     * @return array
     */
    public function explode_name($source_image) {
        $ext = strrchr($source_image, '.');
        $name = ($ext===false) ? $source_image : substr($source_image, 0, -strlen($ext));
        return array('ext'=>$ext, 'name'=>$name);
    }

    /**
     * 根据图片比例处理新的图片宽和高
     * @return bool
     */
    public function image_reproportion() {
        if ( !is_numeric($this->m_width) || !is_numeric($this->m_height) || $this->m_width==0 || $this->m_height==0 ) {
            return false;
        }
        if ( !is_numeric($this->m_orig_width) || !is_numeric($this->m_orig_height) || $this->m_orig_width==0 || $this->m_orig_height==0 ) {
            return false;
        }
        // 根据原始比例和新的高，获得新的宽
        $new_width	= ceil($this->m_orig_width*$this->m_height/$this->m_orig_height);
        // 根据原始比例和新的宽，获得新的高
        $new_height	= ceil($this->m_width*$this->m_orig_height/$this->m_orig_width);
        // 高宽比，如果大于0，那么说明原始应该以高决定新图片的宽和高
        $ratio = (($this->m_orig_height/$this->m_orig_width) - ($this->m_height/$this->m_width));
        if ($this->m_master_dim!='width' && $this->m_master_dim!='height' ) {
            $this->m_master_dim = ($ratio < 0) ? 'width' : 'height';
        }
        if ( ($this->m_width!=$new_width) && ($this->m_height!=$new_height) ) {
            if ( $this->m_master_dim=='height' ) {
                $this->m_width = $new_width;
            }
            else {
                $this->m_height = $new_height;
            }
        }
        return true;
    }

    /**
     * 初始化
     * @return boolean
     */
    public function init(){
        if ( $this->m_source_image=='' ) {
            $this->set_error('源图片不能为空');
            return false;
        }
        if ( !function_exists('getimagesize') ) {
            $this->set_error('图片处理需要GD2库支持');
            return false;
        }
        $this->m_image_library = strtolower($this->m_image_library);
        // 处理图片目录和图片地址
        if ( function_exists('realpath') && @realpath($this->m_source_image)!==false ) {
            $full_source_path = str_replace("\\", "/", realpath($this->m_source_image));
        }
        else {
            $full_source_path = $this->m_source_image;
        }
        $x = explode('/', $full_source_path);
        $this->m_source_image = end($x);
        $this->m_source_folder = str_replace($this->m_source_image, '', $full_source_path);
        // 获取并设置图片属性
        if ( !$this->get_image_properties($this->m_source_folder.$this->m_source_image) ) {
            $this->set_error('获取并设置图片属性失败');
            return false;
        }
        if ( $this->m_new_image=='' ) {
            $this->m_dest_folder = $this->m_source_folder;
            $this->m_dest_image = $this->m_source_image;
        }
        else {
            if (strpos($this->m_new_image, '/')===false && strpos($this->m_new_image, '\\')===false ) {
                // 不包含路径符号
                $this->m_dest_folder = $this->m_source_folder;
                $this->m_dest_image = $this->m_new_image;
            }
            else {
                if ( function_exists('realpath') && @realpath($this->m_new_image)!==false ) {
                    $full_dest_path = str_replace("\\", "/", realpath($this->m_new_image));
                }
                else {
                    $full_dest_path = $this->m_new_image;
                }
                // 是否是图片名称
                if ( !preg_match('#\.(jpg|jpeg|gif|png)$#i', $full_dest_path) ) {
                    $this->m_dest_folder = $full_dest_path.'/';
                    $this->m_dest_image = $this->m_source_image;
                }
                else {
                    $x = explode('/', $full_dest_path);
                    $this->m_dest_image = end($x);
                    $this->m_dest_folder = str_replace($this->m_dest_image, '', $full_dest_path);
                }
            }
        }
        // 目标名称和路径
        if ($this->m_create_thumb===false || $this->m_thumb_marker=='' ) {
            $this->m_thumb_marker = '';
        }
        $xp	= $this->explode_name($this->m_dest_image);
        $filename = $xp['name'];
        $file_ext = $xp['ext'];
        $this->m_full_src_path = $this->m_source_folder.$this->m_source_image;
        $this->m_full_dst_path = $this->m_dest_folder.$filename.$this->m_thumb_marker.$file_ext;
        // 是否保持图像比例
        if ($this->m_maintain_ratio===true && ($this->m_width && $this->m_height)) {
            $this->image_reproportion();
        }
        // 如果没有保持比例
        if ( !$this->m_width ) {
            $this->m_width = $this->m_orig_width;
        }
        if ( !$this->m_height ) {
            $this->m_height = $this->m_orig_height;
        }
        // 设置品质
        $this->m_quality = trim(str_replace("%", "", $this->m_quality));

        if ( $this->m_quality=='' || $this->m_quality==0 || !is_numeric($this->m_quality) ){
            $this->m_quality = 90;
        }
        // 设置x和y的坐标
        $this->m_x_axis = ($this->m_x_axis=='' || !is_numeric($this->m_x_axis)) ? 0:$this->m_x_axis;
        $this->m_y_axis = ($this->m_y_axis=='' || !is_numeric($this->m_y_axis)) ? 0:$this->m_y_axis;
        return true;
    }

    /**
     * 调整图片大小
     */
    public function resize() {
        $protocol = 'image_process_'.$this->m_image_library;
        if ( preg_match('/gd2$/i', $protocol) ) {
            $protocol = 'image_process_gd';
        }
        return $this->$protocol('resize');
    }

    /**
     * 图片裁剪
     */
    public function crop() {
        $protocol = 'image_process_'.$this->m_image_library;
        if ( preg_match('/gd2$/i', $protocol) ) {
            $protocol = 'image_process_gd';
        }
        return $this->$protocol('crop');
    }

    /**
     * 创建GD图片句柄
     * @param string $path			图片地址
     * @param string $image_type	图片类型
     * @return boolean|resource
     */
    public function image_create_gd($path = '', $image_type = '') {
        if ( $path=='' ){
            $path = $this->m_full_src_path;
        }
        if ( $image_type=='' ) {
            $image_type = $this->m_image_type;
        }
        switch ($image_type) {
            case 1 :
                // GIF
                if ( !function_exists('imagecreatefromgif') ) {
                    $this->set_error('图片创建不支持gif格式');
                    return false;
                }
                return imagecreatefromgif($path);
                break;
            case 2 :
                // JPG
                if ( !function_exists('imagecreatefromjpeg') ) {
                    $this->set_error('图片创建不支持jpg格式');
                    return false;
                }
                return imagecreatefromjpeg($path);
                break;
            case 3 :
                // PNG
                if ( !function_exists('imagecreatefrompng') ) {
                    $this->set_error('图片创建不支持png格式');
                    return false;
                }
                return imagecreatefrompng($path);
                break;
        }
        $this->set_error('图片创建不支持除gif,jpg,png意外的格式');
        return false;
    }

    /**
     * GD图像保存
     * @param resource $resource
     * @return boolean
     */
    public function image_save_gd($resource) {
        switch ($this->m_image_type) {
            case 1 :
                if ( !function_exists('imagegif') ) {
                    $this->set_error('保存图像不支持gif格式');
                    return false;
                }
                if ( !@imagegif($resource, $this->m_full_dst_path)) {
                    $this->set_error('保存图像失败[gif]');
                    return false;
                }
                break;
            case 2 :
                if ( !function_exists('imagejpeg') ) {
                    $this->set_error('保存图像不支持jpg格式');
                    return false;
                }
                if ( !@imagejpeg($resource, $this->m_full_dst_path, $this->m_quality) ) {
                    $this->set_error('保存图像失败[jpeg]['.$this->m_full_dst_path.']');
                    return false;
                }
                break;
            case 3 :
                if ( !function_exists('imagepng') ) {
                    $this->set_error('保存图像不支持png格式');
                    return false;
                }
                if ( !@imagepng($resource, $this->m_full_dst_path)) {
                    $this->set_error('保存图像失败[png]');
                    return false;
                }
                break;
            default	:
                $this->set_error('保存图像失败[none]');
                return false;
                break;
        }
        return true;
    }

    /**
     * 使用GD库对图片进行调整大小和裁剪
     * @param string $action
     * @return boolean
     */
    public function image_process_gd($action = 'resize') {
        $v2_override = false;
        // 如果比例不变，并且新的文件名与旧文件名不同的话，那么直接拷贝覆盖
        if ( $this->m_orig_width==$this->m_width && $this->m_orig_height==$this->m_height ) {
            if ( $this->m_source_image!=$this->m_new_image ) {
                if ( @copy($this->m_full_src_path, $this->m_full_dst_path) ) {
                    @chmod($this->m_full_dst_path, $this->m_file_write_mode);
                }
            }
            return true;
        }
        // 根据不同操作，设置不同的值
        if ( $action=='crop' ) {
            // 重新设置源高和宽
            $this->m_orig_width  = $this->m_width;
            $this->m_orig_height = $this->m_height;
            // GD 2.0有个裁剪bug，如果是2.0，设置标识
            if ( $this->gd_version()!==false ) {
                $gd_version = str_replace('0', '', $this->gd_version());
                $v2_override = ($gd_version == 2) ? true : false;
            }
        }
        else {
            // 如果调整大小，那么x y轴必须是0
            $this->m_x_axis = 0;
            $this->m_y_axis = 0;
        }
        boolval($v2_override);
        // 创建图像句柄
        if ( !($src_img=$this->image_create_gd()) ) {
            $this->set_error('创建图像句柄失败');
            return false;
        }
        // 创建图像
        if ( $this->m_image_library=='gd2' && function_exists('imagecreatetruecolor') ) {
            $create	= 'imagecreatetruecolor';
            $copy = 'imagecopyresampled';
        }
        else {
            $create	= 'imagecreate';
            $copy = 'imagecopyresized';
        }
        $dst_img = $create($this->m_width, $this->m_height);
        if ( $this->m_image_type==3 ) {
            // PNG 保持透明
            imagealphablending($dst_img, false);
            imagesavealpha($dst_img, true);
        }
        $copy($dst_img, $src_img, 0, 0, $this->m_x_axis, $this->m_y_axis, $this->m_width, $this->m_height, $this->m_orig_width, $this->m_orig_height);
        if ( !$this->image_save_gd($dst_img) ) {
            $this->set_error('保存图像失败');
            return false;
        }
        // 释放句柄
        imagedestroy($dst_img);
        imagedestroy($src_img);
        // 设置文件权限属性
        @chmod($this->m_full_dst_path, $this->m_file_write_mode);
        return true;
    }

    /**
     * @return string
     */
    public function getMSizeStr(): string
    {
        return $this->m_size_str;
    }

    /**
     * @return string
     */
    public function getMCreateFnc(): string
    {
        return $this->m_create_fnc;
    }

    /**
     * @return string
     */
    public function getMCopyFnc(): string
    {
        return $this->m_copy_fnc;
    }

}